﻿using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using QPZS.ETKA.VinQueryService;
using System;
using System.Collections.Generic;
using System.Linq;
using Wicture.DbRESTFul;
using Wicture.DbRESTFul.Rpc.Client;
using Wicture.DbRESTFul.SQL;
using Wicture.EPC.Common;
using Wicture.QPZS.EPC.Models;

namespace Wicture.QPZS.EPC.Services
{
    public class VinParser
    {
        /// 在vin_seed表中，存入vin前缀与品牌的关联信息，通过下面的SQL可以简单地得到前6位，
        /// 通过这个先做一个匹配，能匹配，再去数据库中检查相应的品牌。
        /// SELECT T.brandCode, GROUP_CONCAT(T.vin SEPARATOR ',')
        /// FROM(SELECT DISTINCT brandCode, substring(vin, 1, 6) as vin FROM vin_seed GROUP BY brandCode, substring(vin, 1, 6)) T
        /// GROUP BY T.brandCode;

        private Dictionary<string, string> EtkaBrandMap = new Dictionary<string, string>
        {
            { "VW", "vw" },
            { "AU", "audi" },
            { "SK", "skoda" },
            { "SE", "seat" },
            { "Seat", "seat" }
        };

        private DbRESTFulRepository repository;

        public VinParser()
        {
            repository = new DbRESTFulRepository();
        }

        private const string EPCServiceV1 = "Wicture.EPC.Service.V1";
        private const string EPCServiceEtka = "Wicture.EPC.Service.Etka";
        private const string EPCServiceBenz = "Wicture.EPC.Service.Benz";

        private static string[] V1Brands = ConfigurationManager.Settings.Variables[EPCServiceV1].Split(",");
        private static string[] EtkaBrands = ConfigurationManager.Settings.Variables[EPCServiceEtka].Split(",");
        private static string[] BenzBrands = ConfigurationManager.Settings.Variables[EPCServiceBenz].Split(",");
        private static LogicalException _vinNotVaildException = new LogicalException("请输入正确的VIN码。", 612);

        public (string serviceName, string brandCode) ParseBrandCode(string vin)
        {
            var (serviceName, brandCode) = VinServiceRule.MatchPrefixRule(vin);
            return (serviceName, brandCode);
        }

        public JObject ParseVinForEtka(RpcRepository rpc, string vin, bool needCheckVin = true)
        {
            if (vin.Length < 17) return null;

            var (serviceName, brandCode) = VinServiceRule.MatchPrefixRule(vin);
            if (serviceName == null || brandCode == null)
            {
                (serviceName, brandCode) = VinServiceRule.MatchRegexRule(vin);
            }

            if (serviceName != null && (brandCode == "vw" || brandCode == "audi"
                                  || brandCode == "skoda" || brandCode == "seat" || brandCode == "porsche"))
            {
                JToken param = new JObject();
                param[ParamFields._vin] = vin;
                param[ParamFields._brandCode] = brandCode;
                try
                {
                    // 如果能查询到，直接返回
                    return QueryVinFromService(rpc, param);
                }
                catch (LogicalException ex)
                {
                    if (ex.ErrorCode == "610")
                    {
                        LoggerManager.Logger.LogInformation("Unable to find vin data from Db, try to query from Online Vin Query service.");
                        if (needCheckVin && !VinHelper.CheckVinVaild(vin))
                        {
                            throw _vinNotVaildException;
                        }
                        if (brandCode == "porsche")
                        {
                            QueryVinFromAgentAndSavePorsche(rpc, vin, brandCode);
                        }
                        else
                        {
                            // 如果没有查到结果，则需要先从通过ETKA Vin查询代理在线查询，再尝试
                            QueryVinFromAgentAndSaveETKA(rpc, vin, brandCode);
                        }

                        return QueryVinFromService(rpc, param);
                    }
                    else
                    {
                        throw ex;
                    }
                }
            }

            return null;
        }

        public JObject ParseVinForRule(RpcRepository rpc, JToken param, bool needCheckVin = true)
        {
            string serverName = null, brandCode = null;
            string vin = param.Value<string>(ParamFields._vin);

            if (vin.Length == 7)
            {
                //宝马支持后7位查询（暂时写死，下一版做配置）
                brandCode = "bmw";// param.Value<string>("brandCode");
                serverName = ParseServiceName(brandCode);
            }
            else if (vin.Length == 8)
            {
                //奔驰支持后8位查询（暂时写死，下一版做配置）
                brandCode = "benz";
                serverName = ParseServiceName(brandCode);
            }
            else
            {
                // 根据规则分析，得到品牌和服务名称
                (serverName, brandCode) = ParseBrandCode(vin);
            }

            // 如果能分析出品牌代码或直接指定品牌，直接通过品牌代码去调用`Get_Model_Info`接口获取数据。
            param[ParamFields._brandCode] = brandCode;
            if (!string.IsNullOrEmpty(brandCode))
            {
                try
                {
                    var data = QueryVinFromService(rpc, param, serverName);

                    //奔驰的VIN码后8位处理
                    if (data != null && data["vins"] != null && data["vins"].Count() > 1)
                    {
                        param[ParamFields._vin] = data["vins"][0].Value<string>("vin");
                        var vinData = QueryVinFromService(rpc, param, serverName);
                        return JObject.FromObject(vinData);
                    }
                    return data;
                }
                catch (LogicalException ex)
                {
                    if (ex.ErrorCode == "610")
                    {
                        if (brandCode == "landrover" || brandCode == "jaguar" || brandCode == "volvo")
                        {
                            LoggerManager.Logger.LogInformation("Unable to find vin data from Db, try to query from Online Vin Query service.");

                            if (needCheckVin && !VinHelper.CheckVinVaild(vin))
                            {
                                throw _vinNotVaildException;
                            }
                            return QueryVinFromOnlineServiceJlr(rpc, param, serverName);
                        }
                        else
                        {
                            throw ex;
                        }
                    }
                    else
                    {
                        throw ex;
                    }
                }
            }

            return null;
        }

        private JObject QueryVinFromOnlineServiceJlr(RpcRepository rpc, JToken param, string serviceName = EPCServiceEtka)
        {
            var data = rpc.Call<JObject>(serviceName, "Get_Vin_Info_Online", param)?.data;
            if (data != null && data[ParamFields._brandCode] == null)
            {
                data[ParamFields._brandCode] = param.Value<string>(ParamFields._brandCode);
            }
            return data;
        }

        private JObject QueryVinFromService(RpcRepository rpc, JToken param, string serviceName = EPCServiceEtka)
        {
            var data = rpc.Call<JObject>(serviceName, "Get_Model_Info", param)?.data;
            if (data != null && data[ParamFields._brandCode] == null)
            {
                data[ParamFields._brandCode] = param.Value<string>(ParamFields._brandCode);
            }
            return data;
        }

        public ETKAVin QueryVinFromAgentAndSaveETKA(RpcRepository rpc, string vin, string brandCode)
        {
            // 通过ETKA Vin查询代理在线查询
            LoggerManager.Logger.LogInformation($"Start call QueryETKA for vin: { vin }.");
            var vinInfo = VinQueryRpcServiceClient.QueryETKA(vin);
            LoggerManager.Logger.LogInformation($"Result from QueryETKA: { JsonConvert.SerializeObject(vinInfo, Formatting.Indented)}.");

            // 调用Rpc将VIN数据存入数据库（指定品牌）
            //vinInfo.Brand = string.IsNullOrEmpty(vinInfo.Brand) ? EtkaBrandMap.FirstOrDefault(k => k.Value == brandCode).Key : vinInfo.Brand;
            //vinInfo.brandCode = EtkaBrandMap.ContainsKey(vinInfo.Brand) ? EtkaBrandMap[vinInfo.Brand] : throw new Exception("Unexpected Brand.");
            vinInfo.brandCode = brandCode;
            rpc.Call<JObject>(EPCServiceEtka, "Insert_Vin_For_Etka", vinInfo);

            return vinInfo;
        }


        public PorscheVin QueryVinFromAgentAndSavePorsche(RpcRepository rpc, string vin, string brandCode)
        {
            // 通过ETKA Vin查询代理在线查询
            LoggerManager.Logger.LogInformation($"Start call QueryPorsche for vin: { vin }.");
            var vinInfo = VinQueryRpcServiceClient.QueryPorsche(vin);
            LoggerManager.Logger.LogInformation($"Result from QueryPorsche: { JsonConvert.SerializeObject(vinInfo, Formatting.Indented)}.");

            vinInfo.brandCode = "porsche";

            // 调用Rpc将VIN数据存入数据库（指定品牌）
            rpc.Call<JObject>(EPCServiceEtka, "Insert_Vin_For_Porsche", vinInfo);

            return vinInfo;
        }


        /// <summary>
        /// 根据brandCode获取serviceName
        /// </summary>
        /// <param name="brandCode"></param>
        /// <returns></returns>
        public string ParseServiceName(string brandCode)
        {
            if (V1Brands.Contains(brandCode, StringComparison.Ordinal))
            {
                return EPCServiceV1;
            }
            else if (BenzBrands.Contains(brandCode, StringComparison.Ordinal))
            {
                return EPCServiceBenz;
            }
            else if (EtkaBrands.Contains(brandCode, StringComparison.Ordinal))
            {
                return EPCServiceEtka;
            }

            return string.Empty;
        }

        private string GetVinSeed(string vin, int lastLength)
        {
            if (vin.Length == 17)
            {
                return $"{vin.Substring(0, 8)}*{vin.Substring(9, lastLength)}";
            }
            else
            {
                throw new InvalidOperationException("The length of vin must be 17.");
            }
        }

        public bool IsLiYangVin(string vin)
        {
            bool ret = false;
            string liyangVinPre = ConfigurationManager.Settings.Variables["LiYang.VinPrefix"];
            if (!string.IsNullOrEmpty(liyangVinPre))
            {
                string[] vinPres = liyangVinPre.Split(new char[] { ',' });
                if (vinPres.Any(e => vin.StartsWith(e)))
                {
                    ret = true;
                }
            }
            return ret;
        }
    }
}
